package com.xiaoxi.bms.service.common.aspect.login;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaoxi.bms.common.exception.BMSException;
import com.xiaoxi.bms.common.response.ApiResponse;
import com.xiaoxi.bms.common.util.AOPUtils;
import com.xiaoxi.bms.common.util.IPUtils;
import com.xiaoxi.bms.controller.log.BmsLogLoginController;
import com.xiaoxi.bms.domain.log.BMSLogLogin;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @ClassName UserLoginAspect
 * @Description 切面处理类
 * @Author xiaoxi
 * @Date 2022/10/30 20:26
 * @Version 1.0
 */
@Aspect
@Component
@Slf4j
public class UserLoginAspect {

    @Resource
    private BmsLogLoginController bmsLogLoginController;

    @Resource
    private ObjectMapper objectMapper;

    @Value("${bms.xiaoxi.log.login-log.enable:false}")
    private boolean loginEnable;

    /**
     * 指定切入点
     * 两种方式：
     * 1. 使用表达式进行匹配 --> 如果切入点目录发生改变就切入不到了 建议使用注解
     * 2. 使用注解，在方法上添加一个注解。然后指定添加了这个注解的方法都是切入点
     */
    @Pointcut("@annotation(com.xiaoxi.bms.service.common.aspect.login.LoginLog)")
    public void loginLogPointCut() {
    }

    /**
     * 后置通知，保存登录成功 日志
     */
    @AfterReturning(pointcut = "loginLogPointCut()", returning = "result")
    public void afterReturningHandler(JoinPoint joinPoint, ApiResponse result) {
        log.info("记录成功登录日志 ： {}", loginEnable);
        if (loginEnable) {
            afterReturningLog(joinPoint, result);
        }
    }

    /**
     * 异常通知，保存登录失败 日志
     *
     * @param joinPoint
     * @param throwable
     */
    @AfterThrowing(value = "loginLogPointCut()", throwing = "throwable")
    public void afterThrowingHandler(JoinPoint joinPoint, Throwable throwable) {
        log.info("记录异常登录日志 ： {}", loginEnable);
        if (loginEnable) {
            afterThrowingLog(joinPoint, throwable);
        }
    }

    /**
     * 记录正常登录日志
     *
     * @param joinPoint
     * @param result
     */
    private void afterReturningLog(JoinPoint joinPoint, ApiResponse result) {
        // 扩充注解 获取内容
        LoginLog annotation = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(LoginLog.class);
        String paramName = annotation.param();

        Object object = AOPUtils.getParamValue(joinPoint, paramName);
        Map<String, Object> objectMap = null;
        try {
            if (object != null) {
                objectMap = getObjectToMap(object);
            }
        } catch (IllegalAccessException e) {
            log.error("转换出现异常：{}", e.getMessage());
            log.error("当前信息：{}", object.toString());
        }
        // 登录日志实体类
        BMSLogLogin bmsLogLogin = new BMSLogLogin();

        // 保存登录成功日志
        if (Objects.nonNull(objectMap)) {
            bmsLogLogin.setUsername((String) objectMap.get("username"));
        }
        bmsLogLogin.setIp(getIp());
        bmsLogLogin.setLoginTime(LocalDateTime.now());
        bmsLogLogin.setStatus("200".equals(result.getCode()));
        bmsLogLogin.setCode(result.getCode());


        try {
            bmsLogLogin.setApiResponse(objectMapper.writeValueAsString(result));
            Map<String, String> message = new HashMap<>();
            message.put("message", result.getMessage());
            bmsLogLogin.setMessage(objectMapper.writeValueAsString(message));

            ApiResponse apiResponse = bmsLogLoginController.addLogLogin(bmsLogLogin);
            if (!"200".equals(apiResponse.getCode())) {
                log.error("登录日志记录异常：{}", apiResponse);
            }

        } catch (JsonProcessingException e) {
            log.error("系统转换Json异常：{} --- {}", e.getMessage(), result);
        }

    }

    /**
     * 记录异常登录日志
     *
     * @param joinPoint
     * @param throwable
     */
    private void afterThrowingLog(JoinPoint joinPoint, Throwable throwable) {

        LoginLog annotation = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(LoginLog.class);

        String paramName = annotation.param();

        Object object = AOPUtils.getParamValue(joinPoint, paramName);

        Map<String, Object> objectMap = null;
        try {
            if (object != null) {
                objectMap = getObjectToMap(object);
            }
        } catch (IllegalAccessException e) {
            log.error("转换出现异常：{}", e.getMessage());
            log.error("当前信息：{}", object.toString());
        }
        // 登录日志实体类
        BMSLogLogin bmsLogLogin = new BMSLogLogin();

        // 保存登录成功日志
        if (Objects.nonNull(objectMap)) {
            bmsLogLogin.setUsername((String) objectMap.get("username"));
        }

        // 当前请求信息
        bmsLogLogin.setIp(getIp());

        BMSException bmsException = (BMSException) throwable;

        ApiResponse apiResponse = ApiResponse.ofException(bmsException);

        try {

            bmsLogLogin.setApiResponse(objectMapper.writeValueAsString(apiResponse));
            Map<String, String> message = new HashMap<>();
            message.put("message", apiResponse.getMessage());
            bmsLogLogin.setMessage(objectMapper.writeValueAsString(message));

            bmsLogLogin.setCode(bmsException.getCode());
            bmsLogLogin.setLoginTime(LocalDateTime.now());
            bmsLogLogin.setStatus(false);

            ApiResponse response = bmsLogLoginController.addLogLogin(bmsLogLogin);
            if (!"200".equals(response.getCode())) {
                log.error("登录日志记录异常：{}", response);
            }

        } catch (JsonProcessingException e) {
            log.error("系统转换Json异常：{} ---- {}", e.getMessage(), apiResponse);
        }

    }

    /**
     * 由于登录日志 -- 简单易懂，这里只需要返回ip地址即可
     *
     * @return
     */
    private String getIp() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        assert requestAttributes != null;
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        return IPUtils.getIpAddr(request);
    }

    //Object转Map
    private Map<String, Object> getObjectToMap(Object obj) throws IllegalAccessException {
        Map<String, Object> map = new HashMap<>();
        Class<?> cla = obj.getClass();
        Field[] fields = cla.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String keyName = field.getName();
            Object value = field.get(obj);
            if (value == null)
                value = "";
            map.put(keyName, value);
        }
        return map;
    }


}
